module iTimer.io.Token ;

import iTimer.Types;


bool HttpTextIsWord(char tok){
	return (tok >= '0'  &&tok <= '9' ) || (tok>= 'a'  && tok <= 'z' ) || (tok >= 'A'  && tok <= 'Z')  ;
}

bool HttpTextIsNumber(char tok) {
	return (tok >= '0'  && tok <= '9' )  ;
}
bool HttpTextIsSpace(char tok) {
	return (tok is ' '  || tok is '\t' || tok is '\r' || tok is '\n' )  ;
}
struct vToken {
	alias typeof(*this)	This ;
	alias typeof(this)	pThis ;
	
	alias bool function(char) isTokFn ;
	private static const char[] FakeEmptyString = "\0" ;
	private {
		char[] 	buf	= "" ;
		int		pos , len  ;
	}
	static assert(vToken.sizeof is 16 );

	void set(char[] tmp) out {
		assert( buf !is null) ;
		assert( buf.length is tmp.length );
	} body {
		if( tmp is null ) {
			buf	= FakeEmptyString[0..0] ;
		} else {
			buf	= tmp ;
		}
		pos	= 0 ;
		len	= tmp.length ;
	}
	
	void reset() in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body  {
		pos	= 0 ;
		len	= buf.length ;
	}

	char[] tok(char _tok ) in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body {
		if( pos is len ){
			return buf[pos..len] ;
		}
		int _pos = pos ;
		for( int i = pos ; i < len; i++){
			if( buf[i] is _tok ){
				pos	= i+1 ;
				return buf[ _pos .. i ] ;
			}
		}
		pos	= len ;
		return buf[ _pos .. len ] ;
	}
	

	char[]  TokT(alias Fn, bool SKIP = true )( bool skip = SKIP ) in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body {
		if( pos is len ){
			return buf[pos..len] ;
		}
		int _pos	= pos ;
		int i 		= pos ;
		int _len	= i ;
		// tok 
		while( i < len ) {
			if(  !Fn(buf[i]) ) {
				_len	= i ;
				break ;
			}
			i++;
		}
		if(  i is len ) {
			pos	= len ;
			if( _len !is i ){
				if( Fn(buf[i-1]) ) {
					_len	= len   ;
				} else {
					_len	= len - 1 ;
				}
			}
			return buf[ _pos .. _len ] ; 
		}
		assert(_len is i);
		
		if( !skip ) {
			pos	= i ;
			return buf[ _pos .. i  ] ; 
		}
		
		// skip 
		while( i < len ) {
			if(  Fn(buf[i])  ) {
				break ;
			}
			i++ ;
		}
		assert(  _len !is i  ) ;
		pos	= i ;
		return buf[ _pos .. _len ] ;
	}
	
	char[]  rTokT(alias Fn, bool SKIP = true )( bool skip = SKIP) in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body {
		if( pos is len ) {
			return buf[pos..len] ;
		}

		int _len  	= len ;
		int i 		= len ;
		int _pos	= i ;
		// tok 
		while( i  > pos ) {
			i-- ;
			if(  !Fn(buf[i]) ) {
				_pos	= i ;
				break ;
			}
		}
		
		if( i is pos ) {	// no data left
			len	=  i ;
			if( i is _pos ){ // find breaker
				assert( !Fn(buf[i]) );
				if( !skip ) {
					// return breaker
					len	= i + 1 ;
				}
				_pos++ ; // skip breaker
			} else { // not find break
				_pos	= pos ;
			}
			if(  _pos < 0 || _len > buf.length ) {
				//version(DEV) log!(__FILE__,__LINE__)("{},{}, {}", _pos, _len, buf);
			}
			assert( _pos >= 0 && _len <= buf.length);
			return buf[ _pos  .. _len] ; 
		}
		
		assert( _pos is i ) ;
		
		if( !skip ) {
			len	= _pos + 1 ;
			return buf[ len .. _len ] ; 
		}
		
		// skip 
		while( i > pos ) {
			i-- ;
			if(  Fn(buf[i])  ) {
				break ;
			}
		}
		if( i is pos ) { // no data left
			assert(false);
			len	= i   ;
		} else {
			len	= i  + 1 ;
		}
		return buf[ _pos + 1 .. _len ] ;
	}
	
	char[] tok(char _tok1 , char _tok2) in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body {
		if( pos is len ){
			return buf[pos..len] ;
		}
		int _pos = pos ;
		for( int i = pos ; i < len; i++){
			if( buf[i] is _tok1 ||  buf[i] is _tok2 ){
				pos	= i+1 ;
				return buf[ _pos .. i ] ;
			}
		}
		pos	= len ;
		return buf[ _pos .. len ] ;
	}
	
	char[] rtok(char _tok ) in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body {
		if( pos is len ){
			return buf[pos..len] ;
		}
		int _len	= len ;
		for( int i = len -1 ;  i >= pos ; --i ) {
			if( buf[i] is _tok ){
				len	= i  ;
				return buf[ i +1 .. _len  ] ;
			}
		}
		len	= pos ;
		return buf[ pos .. _len ] ;
	}
	
	char[] rtok(char _tok1 , char _tok2 ) in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;	
	} body {
		if( pos is len ){
			return buf[pos..len] ;
		}
		int _len	= len ;
		for( int i = len -1 ;  i >= pos ; --i ) {
			if( buf[i] is _tok1 || buf[i] is _tok2 ) {
				len	= i  ;
				return buf[ i +1 .. _len  ] ;
			}
		}
		len	= pos ;
		return buf[ pos .. _len ] ;
	}
	
	bool empty() in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;
	} body {
		return pos is len ;
	}
	
	char[] remain()  in {
		assert( buf !is null) ;
	} out {
		assert( buf !is null) ;
	} body {
		return buf[pos..len] ;
	}
	
	
	char[] toString()  {
		return buf ;
	}
	
	
	alias set 	opCall ;
	
	alias TokT!(HttpTextIsWord)	tokw ;
	alias rTokT!(HttpTextIsWord)	rtokw ;
	
	alias TokT!(HttpTextIsNumber)	tokd ;
	alias rTokT!(HttpTextIsNumber)	rtokd ;
	
	alias TokT!(HttpTextIsSpace, false)	toks ;
	alias rTokT!(HttpTextIsSpace, false)	rtoks;
	
	invariant
        {
		assert( pos >= 0 ) ;
		assert( pos <=len ) ;
		assert( len <=buf.length ) ;
        }
}


struct vTokenz {
	alias typeof(*this)	This ;
	alias typeof(this)	pThis ;
	private {
		static const char[1] FakeEmptyString = "\0" ;
		char*	Pos	= &FakeEmptyString[0] ;
		char*	pos	= &FakeEmptyString[0] ;
		size_t	len, Len ;
	}
	alias bool function(char) isTokFn ;
	
	void set(char[] tmp) out {
		assert( Pos is pos ) ;
	} body {
		if( tmp is null || tmp.length is 0 ) {
			Pos	= &FakeEmptyString[0] ;
			Len	= 0 ;
		} else {
			Pos	= &tmp[0] ;
			Len	= tmp.length  ;
		}
		pos	= Pos ;
		len	= Len ;
	}
	
	void reset() {
		pos	= Pos ;
		len	= Len ;
	}
	
	bool empty() {
		return len is 0 ;
	}
	
	char[] toString()  {
		return Pos[ 0 .. Len ] ;
	}
	
	char[] remain(){
		return pos[ 0 ..  len ] ;
	}
	
	char[] tok(isTokFn fn) in {
		assert(fn !is null);
	} body {
		char*	rpos	= pos + len ;
		char*	_pos = pos ;
		while( !fn(*pos) ) {
			if(  pos is rpos ) {
				break ;
			}
			pos++;
		}
		return _pos[ 0 ..  pos - _pos ] ;
	}
		
	char[] rtok(isTokFn fn) in {
		assert(fn !is null);
	} body {
		char*	rpos	= pos + len ;
		char*	_rpos = rpos ;
		while( !fn(*rpos) ) {
			if(  pos is rpos ) {
				break ;
			}
			rpos--;
		}
		return rpos[ 0 ..  _rpos -  rpos ] ;
	}
	/*
	char[] TokC(T...)(T t) {
		static assert( T.length > 0 ) ;
		static assert(is(T[0]==char)) ;
		
		char*	rpos	= pos + len ;
		char*	_pos = pos ;
		while( *pos !is t[0] ) {
			static if(T.length is 2 ) {
			static assert(is(T[1]==char)) ;
				if(  *pos !is t[1]  ){
					break;
				}
			} else {
				foreach(int i, _T; T[1..$]){
					static assert(is(_T==char)) ;
					if(  *pos !is t[i]  ){
						break;
					}
				}
			}
			if(  pos is rpos ) {
				break ;
			}
			pos++;
		}
		return _pos[ 0 ..  pos - _pos ] ;
	}
	
	char[] rTokC(T...)(T t) {
		static assert( T.length > 0 ) ;
		static assert(is(T[0]==char)) ;
		
		char*	_rpos = rpos ;
		while( *rpos !is t[0] ) {
			static if(T.length is 2 ) {
			static assert(is(T[1]==char)) ;
				if(  *rpos !is t[1]  ){
					break;
				}
			} else {
				foreach(int i, _T; T[1..$]){
					static assert(is(_T==char)) ;
					if(  *rpos !is t[i]  ){
						break;
					}
				}
			}
			if(  pos is rpos ) {
				break ;
			}
			rpos--;
		}
		return  rpos[ 0 ..  _rpos -  rpos ] ;
	}
	
	char[] TokF(alias Fn, bool SKIP = true )(bool skip = SKIP) {
		static assert(is( typeof(Fn) ==function)) ;
		alias ReturnTypeOf!(typeof(Fn)) Ret ;
		alias ParameterTupleOf!(typeof(Fn)) Args ;
		static assert( is( Ret ==bool) ||  tango.core.Traits.isIntegerType!(Ret)  ) ;
		static assert(  Args.length is 1 && is(Args[0]==char) ) ;
		
		char*	_pos 	= pos ;
		while( Fn(*pos) ) {
			if(  pos is rpos ) {
				break ;
			}
			pos++;
		}
		
		char[] ret	 ;
		
		if( pos is rpos ) {
			if( pos is _pos ){
				ret	= _pos[ 0 .. 0 ] ;
			} else {
				ret	= _pos[ 0 ..  pos - _pos +1 ] ;
			}
		} else {
			ret	= _pos[ 0 ..  pos - _pos ] ;
		}
		
		if( skip ) {
			while( !Fn(*pos) ) {
				if(  pos is rpos ) {
					break ;
				}
				pos++;
			}
		}
		return ret ;
	}
	
	
	char[] rTokF(alias Fn, bool SKIP = true )(bool skip = SKIP) {
		static assert(is( typeof(Fn) ==function)) ;
		alias ReturnTypeOf!(typeof(Fn)) Ret ;
		alias ParameterTupleOf!(typeof(Fn)) Args ;
		static assert( is( Ret ==bool) ||  tango.core.Traits.isIntegerType!(Ret)  ) ;
		static assert(  Args.length is 1 && is(Args[0]==char) ) ;
		
		char*	_rpos = rpos ;
		while( Fn(*rpos) ) {
			if(  pos is rpos ) {
				break ;
			}
			rpos--;
		}
		char[]   ret = rpos[ 0 ..  _rpos -  rpos ] ;
		
		if( skip ) {
			while( !Fn(*rpos) ) {
				if(  pos is rpos ) {
					break ;
				}
				rpos--;
			}
		}
		return ret ;
	}
	
	alias set	opCall ;
	
	
	alias TokC!(char)		tok ;
	alias TokC!(char,char)	tok ;
	
	alias rTokC!(char)		rtok ;
	alias rTokC!(char,char)	rtok ;
	
	alias TokF!(HttpTextIsWord) tokw;
	alias rTokF!(HttpTextIsWord) rtokw;
	
	invariant
        {
		assert( pos >= Pos ) ;
		assert( len <= Len ) ;
        }
	*/
}
struct vTok {
	alias typeof(*this)	This ;
	alias typeof(this)	pThis ;
	
	vToken tz ;
	char	tok ;
	
	pThis opCall(void[] tmp, char _tok) {
		tz( cast(char[]) tmp);
		tok	= _tok ;
		return this ;
	}
	
	int opApply (int delegate (ref char[]) dg) {
		int ret ;
		char[] _tmp ;
		while( !tz.empty ){
			_tmp	= tz.tok( tok ) ;
			ret	= dg(_tmp) ;
			if( ret !is 0 ){
				return ret ;
			}
		}
		return ret ;
	}
	
	int opApply (int delegate (int , ref char[]) dg) {
		int  i, ret ;
		char[] _tmp ;
		while( !tz.empty ){
			_tmp	= tz.tok( tok ) ;
			ret	= dg(i, _tmp) ;
			if( ret !is 0 ){
				return ret ;
			}
			i++;
		}
		return ret ;
	}
}


struct vTok2 {
	alias typeof(*this)	This ;
	alias typeof(this)	pThis ;
	
	vToken tz ;
	char	tok1, tok2 ;
	
	pThis opCall(void[] tmp, char _tok1, char _tok2){
		tz( cast(char[]) tmp);
		tok1	= _tok1 ;
		tok2	= _tok2 ;
		return this ;
	}
	
	int opApply (int delegate (ref char[]) dg)   {
		int ret ;
		char[] _tmp ;
		while( !tz.empty ){
			_tmp	= tz.tok( tok1, tok2 ) ;
			ret	= dg(_tmp) ;
			if( ret !is 0 ){
				return ret ;
			}
		}
		return ret ;
	}
	
	int opApply (int delegate (int , ref char[]) dg) {
		int  i, ret ;
		char[] _tmp ;
		while( !tz.empty ){
			_tmp	= tz.tok( tok1, tok2 ) ;
			ret	= dg(i, _tmp) ;
			if( ret !is 0 ){
				return ret ;
			}
			i++;
		}
		return ret ;
	}
}

unittest {
	vToken _tz;
	assert(_tz.tok(' ') == "" );
	assert(_tz.empty);
	_tz("");
	assert(_tz.tok(' ') == "" );
	assert(_tz.empty);
	assert(_tz.tok(' ') == "" );
	
	vToken tz;
	assert(tz.empty);
	assert(tz.tok('\n') == "" );
	assert(tz.rtok('\n') == "" );
	assert(tz.empty);
	
	tz("");
	assert(tz.empty);
	
	tz(";1");
	assert( tz.tokd() == "" );
	assert( tz.tokd() == "1" );
	assert(tz.empty);
	tz.reset;
	assert(tz.tokd(false) == "" );
	assert(tz.tokd(false) == "" );
	assert(tz.tokd() == "" );
	assert(tz.tokd() == "1" );
	assert(tz.empty);
	
	tz("1");
	assert( tz.tokd() == "1" );
	assert(tz.empty);
	tz.reset;
	assert(tz.tokd(false) == "1" );
	assert(tz.empty);
	
	tz("1;");
	assert( tz.tokd() == "1" );
	assert(tz.empty);
	tz.reset;
	assert( tz.tokd(false) == "1" );
	assert(!tz.empty);
	
	tz(";1");
	assert( tz.rtokd() == "1" );
	assert(tz.empty);
	tz.reset;
	assert( tz.rtokd(false) == "1" );
	assert(!tz.empty);
	

	tz(";");
	tz.reset;
	assert(!tz.empty);
	assert(tz.tokd=="");
	assert(tz.empty);
	tz.reset;
	assert(!tz.empty);
	assert(tz.tokd(false)=="");
	assert(!tz.empty);
	
	
	
	tz(";123");
	assert(tz.tokd == "" );
	assert(tz.tokd == "123" );
	
	tz.reset;
	assert(tz.rtokd(false) == "123" );
	assert(!tz.empty);
	
	tz.reset;
	assert(tz.rtokd == "123" );
	assert(tz.empty);
	tz.reset;
	assert(tz.rtokd(false) == "123" );
	assert(!tz.empty);
	
	tz.reset;
	assert(tz.tokd(false) == "" );
	assert(tz.tokd(false) == "" );
	assert(tz.tokd() == "" );
	assert(tz.tokd(false) == "123" );
	
	tz("123;");
	assert(tz.tokd(false) == "123" );
	assert(!tz.empty);
	assert(tz.tokd() == "" );
	assert(tz.empty);
	
	tz("123");
	assert(!tz.empty);
	assert(tz.tokd == "123" );
	tz.reset;
	assert(!tz.empty);
	assert(tz.rtokd == "123" );
	assert(tz.empty);
	
	char[] tmp	= "012;345;678;" ;
	tz(tmp);
	assert( tz.rtokd() == "" );
	assert( tz.tokd() == "012" );
	assert( tz.tokd() == "345" );
	assert( tz.tokd() == "678" );
	assert(tz.empty);
	
	
	tz.reset;
	assert( tz.rtokd() == "" );
	assert( tz.rtokd() == "678" );
	assert( tz.rtokd() == "345" );
	assert( tz.rtokd(false) == "012" );
	assert(tz.empty);
	
	tz("123 \tfed");
	assert( tz.tokd(false) == "123" );
	tz.toks();
	assert( tz.tokw() == "fed" );
	assert(tz.empty);
}